//如果在tsconfig.json中开启了strictPropertyInitialization选项的话
//如果在给类定义属性没有赋值会报错
//属性“name”没有初始化表达式，且未在构造函数中明确赋值
//给属性初始化有两种方式，一种是直接赋初始化表达式，一种是在构造函数中明确赋值
/* class Person{
    name:string='zhangsan';
    age:number
    constructor(age:number){
        this.age = age;
    }
    getName():string{
        return this.name;
    }
}
let p = new Person(18);
console.log(p.name)
console.log(p.getName())
console.log(p.age) */

/**
 * 关于类的类型
 * 当我们编写了一个类之后，其实会得到两种类型
 * 1.构造函数的函数类型 typeof Dog
 * 2.类的实例类型 Dog
 */
/* class Dog{
    static staticName = '类的静态属性'
    instanceName:string ='类的实例的属性'
} */
/*
function Dog() {
    this.instanceName = '类的实例的属性';
}
Dog.staticName = '类的静态属性';
*/
//Dog这个类名也是一个类型，可以用来定义Dog的实例
/* let dogInstance:Dog = new Dog();
//实例类型可以访问实例的属性
console.log(dogInstance.instanceName);
//获取构造函数的类型
type ClassType = typeof Dog;
//在TS中，冒号后面的是类型，等号后面的是值
let varClassType:ClassType=Dog;

function hello(){
}
//有些写法在ts和js都是存在的
let varHello:typeof hello=hello */

/* 
class Component{
    static myName:string="hello";
    myName:string="world";
}
let com=Component;
let c2:Component=new Component();
let f:typeof Component=com;
console.log(c2);
console.log(f);

let c3 = new Dog();
let c4 = Dog; */

//存取器
//在TS中，我们可以通过存取器来改变一个类中属性的读取和赋值行为
/* class User{
    myName:string;
    constructor(myName:string){
        this.myName = myName;
    }
    //getter
    get name(){
        return this.myName;
    }
    set name(value){
        this.myName=value;
    }
}
let user1 = new User('zhangsan');
console.log(user1.myName);
console.log(user1.name); */

//参数属性
//只要在构造函数的参数变量前添加public关键字，就可以自动化此参数变为类的实例属性
/* class User{
    constructor(public myName:string){

    }
}
let user = new User('zhangsan');
console.log(user) */
//readonly 只读
//readonly修饰符修饰的变量只能在构造函数中初始化
//TS中的只在编译阶段检查，运行时不检查了 const
/* class Animal{
    readonly name:string
    constructor(){
        this.name = 'animal'
    }
}
let animal:Animal = new Animal(); */
//无法为“name”赋值，因为它是只读属性
//animal.name = 'zhangsan';

//继承
//子类继承父类后子类实例就拥有了父类中的属性的方法，可能增强代码的复用性
//把子类里的公有方法放在父类中，子类就可以复用
//还可以在父类中定义了抽象的方法，子类进行实现
//还可以在一些公共逻辑放在父类中实现，子类只负责添加额外的逻辑
//如果给构造函数中的参数添加public修饰符的话，相当于给此类的实例添加属性，并赋值为此参数的值
/* class Person{
    constructor(public name:string,public age:number){}
    getName():string{
        return this.name;
    }
    setName(newName:string){
        this.name = newName;
    }
}
class Student extends Person{
    no:number;//学号
    constructor(name:string,age:number,no:number){
        super(name,age);//调用父类的构造函数，并传递name和age两个参数
        this.no = no;
    }
    getNo(){
        return this.no;
    }
    //重写就是子类编写一个父类名称一样的函数，就相当 于重写了父类的函数的了
    //在调用子类实例的此方法的时候，会执行子类重写后的方法
    //可以在方法中通过super调用属性父类的方法
    getName():string{
        //super.getName()指的是调用父类身上的getName方法
        return `子类重写父类的getName方法,父类的值是${super.getName()}`;
    }
}
let s1 = new Student('zhangsan',18,100);
s1.getName();//子类可以调用父类的方法
s1.getNo();//子类也可以调用子类的方法
console.log(s1.getName())
 */
//type意思是定义自定义类型，或者说定义类型的别名
//感觉不太清楚在ts的那些场景中typeof xxx的后面值是什么?什么时候算是ts中的规则,什么时候是js中的规则
/* class TheClass {
    constructor(public name: string, public age: number) { }
}
let jsTypeof = typeof TheClass//js中的typeof;
console.log(`jsTypeof-->`, jsTypeof);
type TsTypeof = typeof TheClass//ts中的typeof;
//“TsTypeof”仅表示类型，但在此处却作为值使用
//console.log(`TsTypeof-->`, TsTypeof);
let tsTypeof: typeof TheClass = TheClass
console.log(`tsTypeof-->`, tsTypeof);
//TheClass放在等号右边表示的是一个值，放在：右边表示是一个类型
let theInstance: TheClass = new TheClass('f', 55)
console.log(`theInstance-->`, theInstance);
//let typeof就是JS中的typeof ,返回的是一个字符串，表示后面值 的类型 "function"
//type typeof就是TS中的typeof ,返回是一个类型
//放在：右边是也是TS中的typeof ,返回的也是一个类型 */
/* 
//类里的修饰符
class Father{
    public name:string;//在子类和其它人都可以任意访问
    protected age:number;//只有父类的子类可以访问，其它人不行
    private money:number;//只有父类能访问，子类和其它人不行
    constructor(name:string,age:number,money:number){
        this.name = name;
        this.age = age;
        this.money=money;
    }
    print(){
        console.log(this.name,age,this.money)
    }
}
class Child extends Father{
    constructor(name:string,age:number,money:number){
       super(name,age,money);
    }
    print(){
        //属性“money”为私有属性，只能在类“Father”中访问
        console.log(this.name,this.age,this.money)
    }
}
let child = new Child('zhangsan',18,100);
console.log(child.name)
//属性“age”受保护，只能在类“Father”及其子类中访问
//console.log(child.age)
//属性“money”为私有属性，只能在类“Father”中访问
console.log(child.money) */

//抽象类和抽象的方法
//抽象类描述一种抽象的概念，无法被实例化，只能被继承
//无法创建抽象类的实例
//抽象类里会有抽象的方法，抽象方法只能在子类中实现 而且必须实现
//抽象方法只能出现在抽象类
//子类可以对抽象方法进行不同的实现
/* abstract class Animal{
   name:string='animal';
   abstract speak():void; 
}
//非抽象类“Dog”没有实现继承自“Animal”类的抽象成员“speak”
class Dog extends Animal{
    speak(): void {
        console.log('汪汪汪')
    }
}
let dog = new Dog();
dog.speak();
class Cat extends Animal{
    speak(): void {
        console.log('喵喵喵')
    }
}
let cat = new Cat();
cat.speak();
 */
/**
 * 访问修饰符 public protected private
 * 只读属性 readonly
 * 静态属性 static
 * 抽象类和抽象方法 abstract
 */

/**
 * 重写(override) VS 重载(overload)
 * 重写是指子类重写继承自父类的方法
 * 重载指的是为了一个函数提供多种类型的定义
 */
/* class Animal {
    name: string = 'animal';
    speak() {
        console.log('Animal')
    };
}
class Cat extends Animal {
    speak(): void {
        console.log('喵喵喵')
    }
}
class Dog extends Animal {
    speak(): void {
        console.log('汪汪汪')
    }
}
function double(x:number):number;
function double(x:string):string;
function double(x:any){
    if(typeof x === 'number'){
        return x*2;
    }
    return x+x;
}
double(2);
double('22') */


//继承和多态
//继承是子类继承父类，子类除了拥有父类的所有特性外，还有一些更加具体的特性
//多态就是由继承产出的 多个不同的子类可以针对同一个行为有不同的实现
